# include <grid.H>


Grid_Node::Grid_Node() : Euclidian_Node<long>(),
              __is_in(false), __is_out(false),
  num_nodes_deflected(0), num_nodes_routed(0), unsorted_counter(0), percent_total(0), packages_deliveried_counter(0), packages_generated_counter(0)
{
  /* Empty */
}

Grid_Node::Grid_Node(const long & num)
  : Euclidian_Node<long>(num), __is_in(false), __is_out(false),
  num_nodes_deflected(0), num_nodes_routed(0), unsorted_counter(0), percent_total(0), packages_deliveried_counter(0), packages_generated_counter(0)
{
  /* Empty */
}

Grid_Node::Grid_Node(Grid_Node * node)
  : Euclidian_Node<long>(node), __is_in(node->__is_in), __is_out(node->__is_out),
  num_nodes_deflected(node->num_nodes_deflected), num_nodes_routed(node->num_nodes_routed), unsorted_counter(node->unsorted_counter), percent_total(node->percent_total),
    packages_deliveried_counter(node->packages_deliveried_counter), packages_generated_counter(node->packages_generated_counter)
{
  /* Empty */
}

Grid_Node::Grid_Node(const Point & point)
  : Euclidian_Node<long>(point),  __is_in(false), __is_out(false),
    num_nodes_deflected(0), num_nodes_routed(0), unsorted_counter(0), percent_total(0), packages_deliveried_counter(0), packages_generated_counter(0)
{
  /* Empty */
}

Grid_Node::Grid_Node(const long & num, const Point & point)
  : Euclidian_Node<long>(num, point),  __is_in(false), __is_out(false),
    num_nodes_deflected(0), num_nodes_routed(0), unsorted_counter(0), packages_deliveried_counter(0), packages_generated_counter(0)
{
  /* Empty */
}

bool & Grid_Node::is_in() { return __is_in; }

bool & Grid_Node::is_out() { return __is_out; }

const unsigned long & Grid_Node::get_num_nodes_deflected() const { return num_nodes_deflected; }

const unsigned long & Grid_Node::get_num_nodes_routed() const { return num_nodes_routed; }

const double & Grid_Node::get_percent_total() const{ return percent_total;}

void Grid_Node::set_percent_total(double &p) { percent_total = p;}

void Grid_Node::inc_num_nodes_deflected() { ++num_nodes_deflected; }

void Grid_Node::inc_num_nodes_routed() { ++num_nodes_routed; }

void Grid_Node::inc_packages_generated() { ++packages_generated_counter; }

const bool Grid_Node::is_io() const { return __is_in and __is_out; }

DynDlist<Package *> & Grid_Node::get_clients_list() { return clients; }

DynDlist<Package *> & Grid_Node::get_queue() { return queue; }

void Grid_Node::add_deliveried(Package * p)
{
  Grid_Node * source = p->get_source_node();

  Deliveried_List * dl = deliveried_packages.search(source);

  ++packages_deliveried_counter;
  if (dl == NULL)
    dl = deliveried_packages.insert(source, Deliveried_List());

  dl->list.append(p);
  //std::cerr << "Se inserto " << p->get_id() << " " << source->to_string() << endl;

  int counter = 0;

  Dnode <Package *> * head = &dl->list;
  Dnode <Package *> * curr = head->get_prev();
  Dnode <Package *> * prev = curr->get_prev();

  while (prev != head and curr->get_data()->get_id() < prev->get_data()->get_id())
    {
      ++counter;
      curr->del();
      prev->append(curr);
      prev = curr->get_prev();
    }

  if (counter > 0)
    ++(dl->counter);

}

const std::string Grid_Node::to_string() const { return gnu::autosprintf("Nodo %ld", get_info()); }

void Grid_Node::reset_node()
{
  num_nodes_deflected = num_nodes_routed = packages_deliveried_counter = packages_generated_counter = 0;

  unsorted_counter = 0;

  percent_total = 0.0;

}

void Grid_Node::add_client(Package * p)
{
  p->reset();
  I(package_target->get_position() not_eq get_position());
  Grid_Node * package_target = p->get_target_node();
  if (package_target->get_position().get_x().get_d() < get_position().get_x().get_d())
    p->get_h_favorable_direction() = West;
  else if (package_target->get_position().get_x().get_d() > get_position().get_x().get_d())
    p->get_h_favorable_direction() = East;
  if (package_target->get_position().get_y().get_d() < get_position().get_y().get_d())
    p->get_v_favorable_direction() = North;
  else if (package_target->get_position().get_y().get_d() > get_position().get_y().get_d())
    p->get_v_favorable_direction() = South;
  clients.append(p);
}

void Grid_Node::put_in_queue(Package * p)
{
  p->is_in_queue() = true;
  queue.append(p);
}

Link::Link() : Euclidian_Arc<Empty_Class>(), dir(Num_Directions)
{
  conf = Configuration::get_instance();
  capacity = conf->get_capacity();

}

Link::Link(const Empty_Class & info) : Euclidian_Arc<Empty_Class>(info),
dir(Num_Directions)
{
    conf = Configuration::get_instance();
    capacity = conf->get_capacity();
}

Link::Link(void * src, void * tgt, const Empty_Class & data) : Euclidian_Arc<Empty_Class>(src, tgt, data),
dir(Num_Directions)
{
    conf = Configuration::get_instance();
    capacity = conf->get_capacity();
}

Link::Link(void * src, void * tgt) : Euclidian_Arc<Empty_Class>(src, tgt),
dir(Num_Directions)
{
    conf = Configuration::get_instance();
    capacity = conf->get_capacity();
}

Direction & Link::get_direction() { return dir; }

const Direction & Link::get_direction() const { return dir; }

Package * Link::get_first_package_from_src()
{
    return packages_from_src.get_first();
}

void Link::add_package_from_src(Package * p)
{
    if(packages_from_src.size() >= capacity){
        //Que excepcion?
        throw std::string("Out_of_range");
    } else{
        packages_from_src.append(p);
    }
}

Package * Link::get_first_package_from_tgt()
{
    return packages_from_tgt.get_first();
}

void Link::add_package_from_tgt(Package * p)
{
    if(packages_from_tgt.size() >= capacity){
        throw std::string("Out_of_range");
    } else{
        packages_from_tgt.append(p);
    }
}

const bool Link::is_busy_for_src() const { return packages_from_src.size() >= capacity; }

const bool Link::is_busy_for_tgt() const { return packages_from_tgt.size() >= capacity; }

void Link::reset()
{
    packages_from_src.empty();
    packages_from_tgt.empty();
}

Link & Link::operator = (const Link & l)
{
    if (&l == this)
        return *this;

    dir = l.dir;

    packages_from_src = l.packages_from_src;
    packages_from_tgt = l.packages_from_tgt;

    return *this;
}

Grid::Grid()
  : Graph(), width(), height() { /* Empty */ }

Grid::Grid(const size_t & w, const size_t & h)
  : Graph(), width(w), height(h) { /* Empty */ }

Grid::Grid(const Grid & grid)
  : Graph(grid), width(grid.width), height(grid.height)
{ /* Empty */ }

Grid::~Grid() { /* Empty */ }

void Grid::set_height(const size_t & h) { height = h; }

const size_t & Grid::get_height() const { return height; }

void Grid::set_width(const size_t & w) { width = w; }

const size_t & Grid::get_width() const { return width; }

void Grid::build_automatic_grid()
{
  Grid_Builder<Grid, Op_Node> ()(*this, width, height);
}

Grid::Node * Grid::insert_grid_node(const int & num, const long & x, const long & y)
{
  std::auto_ptr<Node> node(new Node(num));
  node->get_position() = Point(x, y);
  return Graph::insert_node(node.release());
}

Grid::Arc * Grid::insert_arc(Node * src, Node * tgt)
{
  Arc * arc = Graph::insert_arc(src, tgt);
  if (src->get_position().get_x().get_d() < tgt->get_position().get_x().get_d() and
      src->get_position().get_y().get_d() == tgt->get_position().get_y().get_d())
    arc->get_direction() = East;
  else if (src->get_position().get_x().get_d() > tgt->get_position().get_x().get_d() and
           src->get_position().get_y().get_d() == tgt->get_position().get_y().get_d())
    arc->get_direction() = West;
  else if (src->get_position().get_y().get_d() < tgt->get_position().get_y().get_d() and
           src->get_position().get_x().get_d() == tgt->get_position().get_x().get_d())
    arc->get_direction() = South;
  else if (src->get_position().get_y().get_d() > tgt->get_position().get_y().get_d() and
           src->get_position().get_x().get_d() == tgt->get_position().get_x().get_d())
    arc->get_direction() = North;
  return arc;
}

Direction Grid::get_arc_direction_respect_node(Arc * arc, Node * node)
{
  return get_src_node(arc) == node ? arc->get_direction() : get_reflect_direction(arc->get_direction());
}


Grid::Arc * Grid::get_arc_by_rout(Node * node, const Direction & dir)
{
  for (Node_Arc_Iterator it(node); it.has_current(); it.next())
    {
      Arc * a = it.get_current();
      if (get_src_node(a) == node and dir == a->get_direction())
        return a;
      if (get_tgt_node(a) == node and dir == get_reflect_direction(a->get_direction()))
        return a;
    }
  return NULL;
}

const bool Grid::is_arc_busy_for_node(Arc * arc, Node * node)
{
  return get_src_node(arc) == node ? arc->is_busy_for_src() : arc->is_busy_for_tgt();
}

Grid::Arc * Grid::get_first_free_link(Node * node)
{
  for (Node_Arc_Iterator it(node); it.has_current(); it.next())
    {
      Arc * arc = it.get_current();
      if (not is_arc_busy_for_node(arc, node))
        return arc;
    }
  return NULL;
}

void Grid::set_arc_busy_for_node(Arc * arc, Node * node, Package * p)
{
  if (get_src_node(arc) == node)
    arc->add_package_from_src(p);
  else
    arc->add_package_from_tgt(p);
}

bool Grid::is_arc_horizontal(Arc * arc)
{
  Node * src = get_src_node(arc);
  Node * tgt = get_tgt_node(arc);
  return src->get_position().get_y().get_d() == tgt->get_position().get_y().get_d();
}

bool Grid::is_arc_vertical(Arc * arc)
{
  Node * src = get_src_node(arc);
  Node * tgt = get_tgt_node(arc);
  return src->get_position().get_x().get_d() == tgt->get_position().get_x().get_d();
}

void Grid::reset_all_arcs()
{
  for (Arc_Iterator it(*this); it.has_current(); it.next())
    {
      Arc * arc = it.get_current();
      arc->reset();
    }
}

void Grid::clear()
{
  reset_all_arcs();
  for (Node_Iterator it (*this); it.has_current(); it.next())
    {
      Node * node = it.get_current();
      node->get_clients_list().empty();
      node->get_queue().empty();
      node->get_deliveried_packages().empty();
      node->reset_node();
    }
}
